iT邦幫忙

2023 iThome 鐵人賽

DAY 23
0
Modern Web

JS30 x 鐵人30 x MDN doc系列 第 23

[Day23] - Speech Synthesis(JS30 x 鐵人 30 x MDN)

  • 分享至 

  • xImage
  •  

做一個語音閱讀器,可播放使用者輸入的訊息

要製作語音閱讀器,就要用到瀏覽器內建的SpeechSynthesis API。

  1. 我們先取得本題需要用到的各個節點
const voicesDropdown = document.querySelector('[name="voice"]');
//  音調、速度 及 文字輸入框
const options = document.querySelectorAll('[type="range"], [name="text"]');
//  播放鍵
const speakButton = document.querySelector("#speak");
//  暫停鍵
const stopButton = document.querySelector("#stop");
  1. 因為名字太長且不太好記,晚點又會使用他的一些 method 所以我們用一個變數裝起來方便使用
const synth = window.speechSynthesis;
  1. 接著我們要提取語音包,很直白的使用到getVoices() method
const voices = speechSynthesis.getVoices();

但在 Chrome 中這樣使用後並印出來,你會發現只有一個空陣列裡面什麼也沒有,那是因為 Chrome 語音合成功能需要時間來初始化和載入可用的語音選項。因此為了確保能正確提取語音包,我們要針對synthy再添加一個針對voiceschanged事件的監聽器,當語音完全載入後會觸發loadVoices函式,並將語音選項渲染於列表中,改寫程式碼

// 宣告語音包變數供後面更新
let voices;
//  初始載入頁面即執行(Safari瀏覽器
loadVoices();
//  語音包加載完畢再持行一次(Chrome瀏覽器
synth.addEventListener("voiceschanged", loadVoices);
function loadVoices() {
  //  提取語音包
  voices = synth.getVoices();
  //  創建文檔片段
  const fragment = document.createDocumentFragment();
  //  篩選英文語音,並創建選項節點放入文檔片段裝
  voices
    .filter((voice) => voice.lang.includes("en"))
    .forEach((voice) => {
      const option = document.createElement("option");
      option.value = voice.name;
      option.textContent = `${voice.name} (${voice.lang})`;
      fragment.appendChild(option);
    });
  // 渲染於畫面上<select>tag 中
  voicesDropdown.appendChild(fragment);
}
  1. 使用語音合成:語音包選項有了,那我們要如何啟動它?讓網頁真的說出我們輸入的字呢?答案是使用 SpeechSynthesis 的speak() method也是那麼直白,但要傳入的參數必須是SpeechSynthesisUtterance物件,因此我們先建構一個新的並存放於msg變數之中>。SpeechSynthesisUtterancem 擁有六種屬性分別為lang:語言pitch:音調rate:語速text:內容voice:語音包volume:聲音
//  建構出一個語音訊息物件
const msg = new SpeechSynthesisUtterance();
//  統一設定為英文
msg.lang = "en-US";
  1. 播放及暫停語音合成器:使用同一個函式透過傳入的參數來判斷執行播放及暫停,但不管如何觸發後都取消掉上一次唸到一半的語音合成器。
//  切換播放語音
speakButton.addEventListener("click", toggle);
stopButton.addEventListener("click", () => toggle(false));

function toggle(startOver = true) {
  //  播放前再次設定要唸的內容
  msg.text = document.querySelector("textarea").value;
  //  停止上一次唸到一半的語音合成器
  synth.cancel();
  if (startOver) synth.speak(msg);
}
  1. 調整 SpeechSynthesisUtterance 各項屬性調整:當對應節點的值有變更時,將語音合成器的對應屬性跟著更改
//  調整速度及音調
options.forEach((option) => option.addEventListener("input", changeOptions));
//  調整語音包
voicesDropdown.addEventListener("change", setVoice);

function changeOptions(e) {
  msg[this.name] = this.value;
}

function setVoice(e) {
  msg.voice = voices.find((voice) => voice.name === this.value);
  // 切換就執行播放,使用者體驗會較好
  toggle();
}

👉Github Demo 頁面 👈

👉 好想工作室 15th 鐵人賽看板 👈

參考資料

  1. Javascript 30 官網
    https://javascript30.com/
  2. MDN 官網
    https://developer.mozilla.org/en-US/

上一篇
[Day22] - Follow Along Link Highlighter(JS30 x 鐵人 30 x MDN)
下一篇
[Day24] - Sticky Nav(JS30 x 鐵人 30 x MDN)
系列文
JS30 x 鐵人30 x MDN doc30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言